<?php

Raise::load('core.action.RaiseAction');
Raise::load('core.ui.RaiseTemplate');
Raise::load('core.collection.RaiseCollection');
Raise::load('core.io.file.RaisePath');
Raise::load('control.RaiseAjaxArea');
Raise::load('core.text.html.RaiseHtml');
Raise::load('core.reflection.RaiseVariable');
Raise::load('db.core.RaiseDbModel');
Raise::load('core.asset.RaiseAssetManager');
Raise::load('core.postback.RaisePostback');

/**
 * RaisePage abstract Class
 * defines an abstract page in phpRaise
 *
 * @author Sam-Mauris Yong / hellclanner at live dot com
 * @license http://www.opensource.org/licenses/bsd-license New BSD License
 * @package phpraise.core.ui
 * @since 1.2
 * @abstract
 * @todo load the mobile template and detect if is on mobile.
 */
abstract class RaisePage extends RaiseAction {

    /**
     * File name of this page
     * @var string
     */
    private $filename;
    
    /**
     * Title of this page (can be used as <title>)
     * @var string
     */
    protected $title;

    /**
     * The template to use
     * @var string|RaiseTemplate
     */
    protected $template;
    
    /**
     * The postback controllers that this page will be using
     * @var array|RaiseCollection|string|RaiseString
     */
    protected $usePostback;
    
    /**
     * The model or collection of models that will be used to automatically fill up fields on the page
     * @var RaiseDbModel|RaiseCollection
     */
    protected $model;
    
    /**
     * The management of assets of this page
     * @var RaiseAssetManager
     */
    private $assets;

    /**
     * Constructs a new RaisePage
     * @param RaiseCollection|array $p (optional) A collection of parameters passed into the page
     * @param string $t (optional) Template name or template string to use. If not provided, the class name will be used.
     */
    function __construct($p = null, $t = null) {
        parent::__construct($p);
        $this->setTemplate($t);
        $this->filename = RaisePath::baseName($_SERVER['PHP_SELF']);
        $this->assets = new RaiseAssetManager();
    }

    /**
     * Get or set the title of the page
     * @param string $t (optional) If set, the page title will be set to the new value.
     * @return string
     */
    public function title($t = null) {
        if (func_num_args() == 1) {
            $this->title = $t;
        }
        return $this->title;
    }
    
    /**
     * Get the base filename of this class.
     * @return string
     */
    protected function filename(){
        return $this->filename;
    }
    
    /**
     * Get the manager for assets on this page
     * @return RaiseAssetManager
     */
    public function assets(){
        return $this->assets;
    }
    
    /**
     * Get the template of this RaisePage
     * @return RaiseTemplate
     */
    public function template(){
        return $this->template;
    }
    
    /**
     * Set the template of the page
     * @param RaiseTemplate $t 
     */
    protected function setTemplate($t){
        if ($t === null) {
            // no template provided, search for default template with same name
            $t = get_class($this);
        }
        $this->template = new RaiseTemplate($t);
    }
    
    /**
     * The method to prepare the headers in the page
     * @param RaiseHttpHeaderCollection $headers 
     */
    public function header($headers) {
        $headers->add('Content-Type', 'text/html; charset=UTF-8');
        if ($this->params()->keyExists('ajaxcaller')) {
            $headers->add('Content-Type', 'text/javascript');
        }
    }

    /**
     * Called upon when the page is preparing
     * @param RaiseApplication $app
     */
    protected function prepare($app){
        
    }
    
    public function action($app) {
        /**
         * Prepare Postback usage
         */
        $collection = new RaiseCollection();
        if (RaiseVariable::isType($this->usePostback, array('RaiseString', 'string'))) {
            $collection->add($this->usePostback);
        } elseif (RaiseVariable::isType($this->usePostback, array('RaiseCollection', 'array'))) {
            $collection = $this->usePostback;
        }

        foreach ($collection as $c) {
            $c = $c . 'Postback';
            RaisePostback::prepare($app, $c);
        }
        
        $this->template->fields()->add('page.baseurl', $app->settings()->get('phpRaise', 'baseUrl'));
        $this->prepare($app);
    }

    /**
     * The building of the page.
     * This is where you add all your controls and modify the view of the page.
     * Method is named as 'build' instead of 'construct' to prevent the confusion
     * between PHP object instantiation and the page construction method.
     * @abstract
     */
    protected abstract function build();
    
    /**
     * Define a template tag value or fetch a defined tag value
     * @param string $field The template tag name
     * @param string $value (optional) The value of this field. Omit this if you wish to retrieve the value of the field.
     * @return string The value of the field
     */
    protected function define($field, $value = null){
        if(func_num_args() == 2){
            if(is_callable($value)){
                $value = call_user_func_array($value, array());
            }
            $this->template()->fields()->add($field, $value);
        }else{
            $value = $this->template()->fields()->get($field);
        }
        return $value;
    }

    /**
     * Build and prepare output into the response
     */
    public function output() {
        $output = '';
        ob_start();
        $this->build();
        $output = ob_get_contents();
        ob_end_clean();

        if ($this->model instanceof RaiseDbModel) {
            $name = get_class($this->model);
            foreach ($this->model as $k => $v) {
                $this->template->fields()->add($name . '.' . $k, $v);
            }
        } elseif ($this->model instanceof RaiseCollection) {
            foreach ($this->model as $name => $model) {
                if($model instanceof RaiseCollection){
                    foreach($model as $m){
                        foreach ($m as $k => $v) {
                            $this->template->fields()->add($name . '.' . $k, $v);
                        }
                    }
                }elseif($model instanceof RaiseDbModel){
                    foreach ($model as $k => $v) {
                        $this->template->fields()->add($name . '.' . $k, $v);
                    }
                }
            }
        }

        if ($this->params()->keyExists('ajaxcaller')) {
            $id = $this->params()->get('ajaxcaller');
            $control = $this->findRaiseAjaxAreaById($id);
            if ($control && $control instanceof RaiseAjaxArea) {
                $control->isAjax(true);
                return $control;
            } else {
                throw new RaiseInvalidRequestException('The RaiseAjaxArea control of $clientId \'' . $id . '\' was not found.');
            }
        } else {
            $this->template->fields()->add('page.assets.script', $this->assets->script());
            $this->template->fields()->add('page.assets.style', $this->assets->style());
            $this->template->fields()->add('page.title', $this->title());
            $this->template->fields()->add('page.doctype', RaiseHtml::$selectedDocType);
            if (!$this->template->fields()->get('page.content')) {
                $this->template->fields()->add('page.content', $output);
            }

            $html = $this->template->parse();

            return $html;
        }
    }

    /**
     * Find a RaiseAjaxArea by its client ID
     * @param string $id The ID of the RaiseAjaxArea
     * @return RaiseAjaxArea
     */
    private function findRaiseAjaxAreaById($id){
        foreach($this->template->fields() as $v){
            if($v instanceof RaiseAjaxArea && $v->clientId() == $id){
                return $v;
            }
        }
        return null;
    }

}